﻿#define Support_Point_Enter_Exit
#define Support_Double_Click
#define Support_Long_Press
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

namespace Client
{
    /// <summary>
    /// UI触摸事件监听
    /// </summary>
    public class UITouchEventListener :
        MonoBehaviour,
        IEventSystemHandler,
#if Support_Point_Enter_Exit
        IPointerEnterHandler, IPointerExitHandler,
#endif
        IPointerDownHandler, IPointerUpHandler,
        IUpdateSelectedHandler, ISelectHandler,
        IDragHandler, IEndDragHandler, IDropHandler, IBeginDragHandler
    {
        /// <summary>
        /// 长按延迟限制
        /// </summary>
        private const float LongPressDelay = 0.5f;

        /// <summary>
        /// 双击时间限制，应小于等于长按延迟
        /// </summary>
        private const float DoubleClickLimit = 0.5f;

#if Support_Point_Enter_Exit
        #region enter, exit
        private Action<GameObject> onEnter;
        private Action<GameObject> onExit;

        public void SetPointEnterExit(Action<GameObject> onEnter, Action<GameObject> onExit)
        {
            this.onEnter = onEnter;
            this.onExit = onExit;
        }
        public void ClearPointEnterExit()
        {
            this.onEnter = null;
            this.onExit = null;
        }

        public void OnPointerEnter(PointerEventData eventData)
        {
            if (onEnter != null)
            {
                onEnter(gameObject);
            }
        }
        public void OnPointerExit(PointerEventData eventData)
        {
            if (onExit != null)
            {
                onExit(gameObject);
            }
        }
        #endregion
#endif


        #region down, up, click, longPress, doubleClick
        private Action<GameObject> onDown;
        private Action<GameObject> onUp;
        private Action<GameObject> onClick;

        public void SetClick(Action<GameObject> onClick, Action<GameObject> onDown = null, Action<GameObject> onUp = null)
        {
            this.onClick = onClick;
            this.onDown = onDown;
            this.onUp = onUp;
        }
        public void ClearClick()
        {
            this.onClick = null;
            this.onDown = null;
            this.onUp = null;
        }

        public void OnPointerDown(PointerEventData eventData)
        {
            GameObject go = ExecuteEvents.GetEventHandler<IPointerDownHandler>(this.gameObject);
            if (go != null)
            {
                RectTransform rectTrans = go.GetComponent<RectTransform>();
                var worldRect = UIHelper.GetChildRaycastWorldRect(rectTrans);
                Vector2 max = RectTransformUtility.WorldToScreenPoint(eventData.enterEventCamera, worldRect.max);
                Vector2 min = RectTransformUtility.WorldToScreenPoint(eventData.enterEventCamera, worldRect.min);
                Vector2 v1 = RectTransformUtility.WorldToScreenPoint(eventData.enterEventCamera, new Vector2(worldRect.xMax, worldRect.yMin));
                Vector2 v2 = RectTransformUtility.WorldToScreenPoint(eventData.enterEventCamera, new Vector2(worldRect.xMin, worldRect.yMax));

                float xMin = min.x < max.x ? min.x : max.x;
                float xMax = min.x > max.x ? min.x : max.x;
                float yMin = min.y < max.y ? min.y : max.y;
                float yMax = min.y > max.y ? min.y : max.y;
                rect = new Rect(xMin, yMin, xMax - xMin, yMax - yMin);
            }

            if (onDown != null)
            {
                onDown(gameObject);
            }

#if Support_Long_Press
            if (onLongPress != null)
            {
                longPressTriggerd = false;

                if (coLongPress != null)
                {
                    StopCoroutine(coLongPress);
                }
                coLongPress = StartCoroutine(LongPress(LongPressDelay));
            }
#endif
        }
        public void OnPointerUp(PointerEventData eventData)
        {
            if (!(eventData.dragging && eventData.IsPointerMoving()) && rect.Contains(eventData.position, false))
            {
#if Support_Double_Click
                if (onDoubleClick != null)
                {
                    TryDoubleClickEvent(eventData);
                }
                else
#endif
                {
                    DoSingleClickEvent();
                }
            }

            if (onUp != null)
            {
                onUp(gameObject);
            }

#if Support_Long_Press
            if (coLongPress != null)
            {
                StopCoroutine(coLongPress);
                coLongPress = null;
            }
#endif
        }

        private Rect rect;
        private void DoSingleClickEvent()
        {
            if (onClick != null)
            {
#if Support_Long_Press
                if (!longPressTriggerd)
#endif
                {
                    onClick(gameObject);
                }
            }
        }

#if Support_Double_Click
        /// <summary>
        /// 连续两次的touchUp，是否构成双击
        /// </summary>
        private bool doubleClickFlag = false;
        private int m_idUp = 0;
        private float lastDoubleClickTryTime = float.NegativeInfinity;
        private Action<GameObject> onDoubleClick;

        public void SetDoubleClick(Action<GameObject> onDoubleClick)
        {
            this.onDoubleClick = onDoubleClick;
        }
        public void ClearDoubleClick()
        {
            this.onDoubleClick = null;
        }

        private void TryDoubleClickEvent(PointerEventData eventData)
        {
            m_idUp++;

            if (lastDoubleClickTryTime + DoubleClickLimit > Time.realtimeSinceStartup)
            {
#if Support_Long_Press
                if (!longPressTriggerd)
#endif
                {
                    doubleClickFlag = true;
                    lastDoubleClickTryTime = float.NegativeInfinity;
                    onDoubleClick(gameObject);
                }
#if Support_Long_Press
                else
                {
                    lastDoubleClickTryTime = Time.realtimeSinceStartup;
                }
#endif
            }
            else
            {
                lastDoubleClickTryTime = Time.realtimeSinceStartup;
                StartCoroutine(TrySingleClickEvent(eventData, m_idUp));
            }
        }
        private IEnumerator TrySingleClickEvent(PointerEventData eventData, int idUp)
        {
            yield return new WaitForSeconds(DoubleClickLimit);
            if (idUp == m_idUp && !doubleClickFlag)
            {
                DoSingleClickEvent();
            }
        }
#endif

#if Support_Long_Press
        /// <summary>
        /// 单次 touchDown与touchUp，是否触发长按
        /// </summary>
        private bool longPressTriggerd = false;
        private Action<GameObject> onLongPress;
        private Coroutine coLongPress;

        public void SetLongPress(Action<GameObject> onLongPress)
        {
            this.onLongPress = onLongPress;
        }
        public void ClearLongPress()
        {
            this.onLongPress = null;
        }

        private IEnumerator LongPress(float t)
        {
            yield return new WaitForSeconds(t);
            if (onLongPress != null)
            {
                longPressTriggerd = true;
                onLongPress(gameObject);
            }
        }
#endif
#endregion

        #region select, updateselect
        private Action<GameObject> onSelect;
        private Action<GameObject> onUpdateSelect;

        public void SetSelect(Action<GameObject> onSelect, Action<GameObject> onUpdateSelect)
        {
            this.onSelect = onSelect;
            this.onUpdateSelect = onUpdateSelect;
        }
        public void ClearSelect()
        {
            this.onSelect = null;
            this.onUpdateSelect = null;
        }

        public void OnSelect(BaseEventData eventData)
        {
            if (onSelect != null)
            {
                onSelect(gameObject);
            }
        }
        public void OnUpdateSelected(BaseEventData eventData)
        {
            if (onUpdateSelect != null)
            {
                onUpdateSelect(gameObject);
            }
        }
#endregion

#region drag
        private Action<GameObject, PointerEventData> onBeginDrag;
        private Action<GameObject, PointerEventData> onDrag;
        private Action<GameObject, PointerEventData> onDrop;
        private Action<GameObject, PointerEventData> onDragOut;

        public void SetDrag(
            Action<GameObject, PointerEventData> onBeginDrag,
            Action<GameObject, PointerEventData> onDrag,
            Action<GameObject, PointerEventData> onDrop,
            Action<GameObject, PointerEventData> onDragOut)
        {
            this.onBeginDrag = onBeginDrag;
            this.onDrag = onDrag;
            this.onDrop = onDrop;
            this.onDragOut = onDragOut;
        }
        public void ClearDrag()
        {
            this.onBeginDrag = null;
            this.onDrag = null;
            this.onDrop = null;
            this.onDragOut = null;
        }

        public void OnBeginDrag(PointerEventData eventData)
        {
            if (onBeginDrag != null)
            {
                onBeginDrag(gameObject, eventData);
            }
        }
        public void OnDrag(PointerEventData eventData)
        {
            if (onDrag != null)
            {
                onDrag(gameObject, eventData);
            }
        }
        public void OnDrop(PointerEventData eventData)
        {
            if (onDrop != null)
            {
                onDrop(gameObject, eventData);
            }
        }
        public void OnEndDrag(PointerEventData eventData)
        {
            if (onDragOut != null)
            {
                onDragOut(gameObject, eventData);
            }
        }
#endregion

        public void ClearAll()
        {
#if Support_Point_Enter_Exit
            onEnter = null;
            onExit = null;
#endif

            onDown = null;
            onUp = null;
            onClick = null;

#if Support_Double_Click
            onDoubleClick = null;
#endif
#if Support_Long_Press
            onLongPress = null;
#endif

            onSelect = null;
            onUpdateSelect = null;

            onDrag = null;
            onDragOut = null;
            onDrop = null;
            onBeginDrag = null;
        }

        protected void OnDestroy()
        {
            ClearAll();
        }
    }
}
